// Entity

Assault.Entity = {}

// New

function Assault.Entity:New(Class)
	if not (Assault.Entities.List[Class]) then return false end
	
	// Table
	
	local Table = {}
	
	// Meta table
	
	setmetatable(Table, self)
	
	// Index
	
	self.__index = self
	
	// Merge
	
	table.Merge(Table, Assault.Entities.List[Class])
	
	// Entity
	
	Table.Entity            = {}
	Table.Entity.Class      = Class
	Table.Entity.Index      = Assault.Entities.CurrentIndex
	Table.Entity.Position   = Assault.Vector:New(0, 0)
	Table.Entity.Color      = Color(255, 255, 255, 255)
	Table.Entity.Mass       = 25
	Table.Entity.Rotation   = 0
	Table.Entity.Health     = -1
	Table.Entity.Material   = "vgui/white"
	Table.Entity.Name       = "Entity"
	Table.Entity.Force      = {Direction = 0, Speed = 0}
	Table.Entity.Size       = {W = 0, H = 0}
	Table.Entity.Variables  = {}
	Table.Entity.Parent     = {Entity = false, Rotation = 0, Difference = 0}
	Table.Entity.Physics    = true
	Table.Entity.Collisions = true
	
	// Increase index
	
	Assault.Entities.CurrentIndex = Assault.Entities.CurrentIndex + 1
	
	// Return table
	
	return Table
end

// Set entity position

function Assault.Entity:EntitySetPosition(Vector)
	self.Entity.Position = Vector
end

// Get entity position

function Assault.Entity:EntityGetPosition()
	return self.Entity.Position
end

// Entity get position center

function Assault.Entity:EntityGetPositionCenter()
	local Position = self:EntityGetPosition()
	local Size = self:EntityGetSize()
	
	// X and Y
	
	local X = Position.X + Size.W
	local Y = Position.Y + Size.H
	
	// Return vector
	
	return Assault.Vector:New(X, Y)
end

// Set entity material

function Assault.Entity:EntitySetMaterial(Material)
	self.Entity.Material = Material
end

// Get entity material

function Assault.Entity:EntityGetMaterial()
	return self.Entity.Material
end

// Set entity color

function Assault.Entity:EntitySetColor(Color)
	self.Entity.Color = Color
end

// Get entity color

function Assault.Entity:EntityGetColor()
	return self.Entity.Color
end

// Get entity index

function Assault.Entity:EntityGetIndex()
	return self.Entity.Index
end

// Set entity size

function Assault.Entity:EntitySetSize(W, H)
	self.Entity.Size = {W = W, H = H}
end

// Get entity size

function Assault.Entity:EntityGetSize()
	return self.Entity.Size
end

// Set entity parent

function Assault.Entity:EntitySetParent(Parent)
	// Entity get distance
	
	local Len = self:EntityGetDistance(Parent)
	
	// Set entity parent
	
	self.Entity.Parent = {Entity = Parent, Rotation = self:EntityGetRotation(), Difference = Len}
end

// Get entity parent

function Assault.Entity:EntityGetParent()
	return self.Entity.Parent
end

// Set entity mass

function Assault.Entity:EntitySetMass(Mass)
	self.Entity.Mass = Mass
end

// Get entity mass

function Assault.Entity:EntityGetMass()
	return self.Entity.Mass
end

// Set entity force

function Assault.Entity:EntitySetForce(Direction, Speed)
	self.Entity.Force = {Direction = Direction, Speed = Speed}
end

// Get entity force

function Assault.Entity:EntityGetForce()
	return self.Entity.Force
end

// Set entity variable

function Assault.Entity:EntitySetVariable(Key, Value)
	self.Entity.Variables[Key] = Value
end

// Get entity variable

function Assault.Entity:EntityGetVariable(Key)
	return self.Entity.Variables[Key]
end

// Set entity rotation

function Assault.Entity:EntitySetRotation(Rotation)
	self.Entity.Rotation = Rotation
end

// Get entity rotation

function Assault.Entity:EntityGetRotation()
	return self.Entity.Rotation
end

// Get entity class

function Assault.Entity:EntityGetClass()
	return self.Entity.Class
end

// Set entity name

function Assault.Entity:EntitySetName(String)
	self.Entity.Name = String
end

// Get entity name

function Assault.Entity:EntityGetName()
	return self.Entity.Name
end

// Set entity physics

function Assault.Entity:EntitySetPhysics(Bool)
	self.Entity.Physics = Bool
end

// Get entity physics

function Assault.Entity:EntityGetPhysics()
	return self.Entity.Physics
end

// Set entity collisions

function Assault.Entity:EntitySetCollisions(Bool)
	self.Entity.Collisions = Bool
end

// Get entity collisions

function Assault.Entity:EntityGetCollisions()
	return self.Entity.Collisions
end

// Set entity health

function Assault.Entity:EntitySetHealth(Amount)
	self.Entity.Health = Amount
end

// Get entity health

function Assault.Entity:EntityGetHealth()
	return self.Entity.Health
end

// Entity damage health

function Assault.Entity:EntityDamageHealth(Attacker, Inflictor, Damage)
	local HP = self:EntityGetHealth()
	
	// Set entity health
	
	if (HP != -1) then
		self:EntitySetHealth(HP - Damage)
		
		// Check is below or equal to 0
		
		if (self:EntityGetHealth() <= 0) then
			self:OnEntityBreak()
		end
	end
	
	// On entity take damage
	
	self:OnEntityTakeDamage(Attacker, Inflictor, Damage)
end

// Get entity max length

function Assault.Entity:EntityGetMaxLength()
	local Size = self:EntityGetSize()
	
	// W and H
	
	local W = Size.W ^ 2
	local H = Size.H ^ 2
	
	// Result
	
	local Result = math.sqrt(W + H)
	
	// Return result
	
	return Result
end

// Entity get nearest point length

function Assault.Entity:EntityGetNearestPointLength(Rotation)
	local A = self:EntityGetNearestPoint(Rotation)
	local B = self:EntityGetPositionCenter()
	
	// Len
	
	local Len = A:GetVectorDistance(B)
	
	// Return len
	
	return Len
end

// Entity get nearest point

function Assault.Entity:EntityGetNearestPoint(Rotation)
	local Position = self:EntityGetPosition()
	local Size = self:EntityGetSize()
	
	// X and Y
	
	local X = Position.X + (Size.W / 2)
	local Y = Position.Y + (Size.H / 2)
	
	// Sin and Cos
	
	local Sin = math.sin(math.rad(Rotation)) * (Size.W / 2)
	local Cos = math.cos(math.rad(Rotation)) * (Size.H / 2)
	
	// X and Y
	
	X = X - Sin
	Y = Y - Cos
	
	// Return X and Y
	
	return Assault.Vector:New(X, Y)
end

// Entity get distance

function Assault.Entity:EntityGetDistance(Entity)
	local A = self:EntityGetPosition()
	local B = Entity:EntityGetPosition()
	
	// Return len
	
	return A:GetVectorDistance(B)
end

// Get front position

function Assault.Entity:GetFrontPosition(Amount)
	local Rotation = self:EntityGetRotation()
	local Position = self:EntityGetPosition()
	
	// Sin and Cos
	
	local Sin = math.sin(math.rad(Rotation)) * Amount
	local Cos = math.cos(math.rad(Rotation)) * Amount
	
	// X and Y
	
	local X = Position.X - Sin
	local Y = Position.Y - Cos
	
	// Return vector
	
	return Assault.Vector:New(X, Y)
end

// Entity paint default

function Assault.Entity:EntityPaintDefault()
	local Material = self:EntityGetMaterial()
	
	// ID
	
	local ID = surface.GetTextureID(Material)
	
	// Size, color, position and rotation of the entity
	
	local Size = self:EntityGetSize()
	local Color = self:EntityGetColor()
	local Position = self:EntityGetPosition()
	local Rotation = self:EntityGetRotation()
	
	// Set texture and draw color then draw the textured rect rotated
	
	surface.SetTexture(ID)
	surface.SetDrawColor(Color.r, Color.g, Color.b, Color.a)
	surface.DrawTexturedRectRotated(Position.X + (Size.W / 2), Position.Y + (Size.H / 2), Size.W, Size.H, Rotation)
	
	// New
	
	local New = self:EntityGetNearestPoint(Rotation)
	
	// Vector
	
	local Vector = self:EntityGetPositionCenter()
	
	// Draw line
	
	surface.SetDrawColor(0, 0, 0, 255)
	surface.DrawLine(Vector.X, Vector.Y, New.X, New.Y)
end

// On entity paint

function Assault.Entity:OnEntityPaint()
	self:EntityPaintDefault()
end

// On entity created

function Assault.Entity:OnEntityCreated()
end

// On entity think

function Assault.Entity:OnEntityThink()
end

// On entity can collide

function Assault.Entity:OnEntityCanCollide(Entity)
	return true
end

// On entity break

function Assault.Entity:OnEntityBreak()
	self:EntityBreak()
end

// On entity used

function Assault.Entity:OnEntityUsed(Entity)
end

// On entity hurt

function Assault.Entity:OnEntityTakeDamage(Attacker, Inflictor, Damage)
end

// On player key pressed

function Assault.Entity:OnPlayerKeyPress(Key)
end

// On player key released

function Assault.Entity:OnPlayerKeyRelease(Key)
end

// On handle parent

function Assault.Entity:OnHandleParent()
	local Parent = self:EntityGetParent()
	
	// Check if the entity has a valid parent
	
	if (Parent.Entity) then
		local Rotation = Parent:EntityGetRotation()
		local Position = Parent:EntityGetRotation()
		
		// Sin and Cos
		
		local Sin = math.sin(math.rad(Rotation + Parent.Rotation)) * Parent.Difference
		local Cos = math.cos(math.rad(Rotation + Parent.Rotation)) * Parent.Difference
		
		// X and Y
		
		local X = Position.X - Sin
		local Y = Position.Y - Cos
		
		// Vector
		
		local Vector = Assault.Vector:New(X, Y)
		
		// Set entity position
		
		self:EntitySetRotation(Rotation + Parent.Rotation)
		self:EntitySetPosition(Vector)
	end
end

// On handle physics

function Assault.Entity:OnHandlePhysics()
	local Mass = self:EntityGetMass()
	local Force = self:EntityGetForce()
	local Physics = self:EntityGetPhysics()
	local Position = self:EntityGetPosition()
	local Parent = self:EntityGetParent()
	
	// Check physics
	
	if (!Physics or Mass == -1 or Parent.Entity) then return false end
	
	// Check speed
	
	if (Force.Speed > 0) then
		for I = 1, Force.Speed do
			local Sin = math.sin(math.rad(Force.Direction)) * I
			local Cos = math.cos(math.rad(Force.Direction)) * I
			
			// X and Y
			
			local X = Position.X - Sin
			local Y = Position.Y - Cos
			
			// Vector
			
			local Vector = Assault.Vector:New(X, Y)
			
			// Check for collision
			
			local Collision = self:EntityCheckForCollisions(Vector)
			
			// Check if the new position will collide with any other entities
			
			if (Collision) then
				return self:EntitySetForce(-Force.Direction, Force.Speed / 8)
			else
				// Set entity position
				
				self:EntitySetPosition(Vector)
				
				// Subtract
				
				local Subtract = math.Clamp(Mass / 5000, 0, 0.99)
				
				// Set entity force
				
				return self:EntitySetForce(Force.Direction, Force.Speed * (0.99 - Subtract))
			end
		end
	end
end

// On bullet hit

function Assault.Entity:OnBulletHit(Bullet)
	Bullet:EntityEmitSound("physics/metal/metal_solid_impact_bullet1.wav", Assault.Level.Player)
end

// On player mouse pressed

function Assault.Entity:OnPlayerMousePressed(Mouse)
end

// On player mouse released

function Assault.Entity:OnPlayerMouseReleased(Mouse)
end

// Entity can collide

function Assault.Entity:EntityCanCollide(Entity)
	local First = self:OnEntityCanCollide(Entity)
	local Second = Entity:OnEntityCanCollide(self)
	
	// Check first and second
	
	if (!First or !Second) then return false end
	
	// First
	
	local First = self:EntityGetCollisions()
	local Second = Entity:EntityGetCollisions()
	
	// Check first and second
	
	if (!First or !Second) then return false end
	
	// Return true
	
	return true
end

// Entity shoot bullets

function Assault.Entity:EntityShootBullets(Damage, Amount, Spread, Speed, Force, Color)
	local Rotation = self:EntityGetRotation()
	local Len = self:EntityGetNearestPointLength(Rotation)
	local Position = self:GetFrontPosition(Len)
	
	// Create bullets
	
	for I = 1, Amount do
		local Entity = Assault.Entity:New("prop_bullet")
		
		// Position
		
		Entity:EntitySetPosition(Position.X, Position.Y)
		Entity:EntitySetSize(2, 16)
		Entity:EntitySetRotation(Rotation + math.random(-Spread, Spread))
		Entity:EntitySetColor(Color)
		
		// Variables
		
		Entity:EntitySetVariable("Speed", Speed)
		Entity:EntitySetVariable("Creator", self)
		Entity:EntitySetVariable("Damage", Damage)
		Entity:EntitySetVariable("Force", Force)
		
		// Create
		
		Entity:Create()
		
		// Increase spread
		
		Spread = Spread + Spread
	end
end

// Check for collisions

function Assault.Entity:EntityCheckForCollisions(Vector, Ignore)
	Ignore = Ignore or {}
	
	// Size A
	
	local Rotation = self:EntityGetRotation()
	local Size = self:EntityGetSize()
	
	// Loop
	
	for K, V in pairs(Assault.Entities.Spawned) do
		if (V != self) then
			local Found = table.HasValue(Ignore, V)
			
			// Check if the entity is ignored
			
			if (!Found) then
				local Collide = self:EntityCanCollide(V)
				
				// Check if entities can collide
				
				if (Collide) then
					local Position = V:EntityGetPosition()
					local Dimensions = V:EntityGetSize()
					
					// Check if the X and Y are in the boundries of the entity
					
					local Boundries = Vector:VectorInsideVector(Size.W, Size.H, Position, Dimensions.W, Dimensions.H)
					
					// Return the entity
					
					if (Boundries) then
						return V
					end
				end
			end
		end
	end
	
	// Return false
	
	return false
end

// Entity emit sound

function Assault.Entity:EntityEmitSound(Sound, Entity)
	local Position = self:EntityGetPosition()
	
	// Sound
	
	Assault.Game.Sound(Position, Entity, Sound)
end

// Entity explode

function Assault.Entity:EntityExplode()
	local Position = self:EntityGetOrigin()
	local Size = self:EntityGetSize()
	
	// Explosion
	
	Assault.Entities.EntitiesQuickCreate("effect_explosion", Position, Size.W, Size.H)
	
	// Entity remove
	
	self:EntityRemove()
end

// Entity break

function Assault.Entity:EntityBreak()
	local Position = self:EntityGetOrigin()
	local Size = self:EntityGetSize()
	
	// Explosion
	
	Assault.Entities.EntitiesQuickCreate("effect_break", Position, Size.W, Size.H)
	
	// Entity remove
	
	self:EntityRemove()
end

// Remove entity

function Assault.Entity:EntityRemove()
	Assault.Entities.Spawned[self.Entity.Index] = nil
end

// Create

function Assault.Entity:Create()
	Assault.Entities.Spawned[self.Entity.Index] = self
	
	// On created
	
	self:OnEntityCreated()
end